#include "../pch.h"
#include "REFXMLName.h"
#include "REFXMLElementList.h"

namespace BFX
{

REFXMLElement::REFXMLElement(REFXMLName* pName, REFXMLDocument* pDoc) :
	REFXMLLinkedNode(pDoc)
{
	BFX_ENSURE (pName != NULL);
	BFX_ENSURE (!pName->GetLocalName().IsEmpty());

	m_pParentNode = NULL;
	m_pLastChild = NULL;

	m_pName = pName;
	m_pName->AddRef();
}

REFXMLElement::~REFXMLElement()
{
	m_pName->Release();
}

UniString REFXMLElement::GetName() const
{
	return m_pName->GetName();
}

UniString REFXMLElement::NamespaceURI() const
{
	return m_pName->GetNamespaceURI();
}

UniString REFXMLElement::GetPrefix() const
{
	return m_pName->GetPrefix();
}

void REFXMLElement::SetPrefix(const UniString& str)
{
	REFXMLName* pNewName = m_pName->GetOwnerDocument()->AddXmlName(str, GetLocalName(), GetNamespaceURI());
	pNewName->AddRef();
	m_pName->Release();
	m_pName = pNewName;
}

UniString REFXMLElement::GetLocalName() const
{
	return m_pName->GetLocalName();
}

XMLNodeType REFXMLElement::GetNodeType() const
{
	return XMLNodeType_Element;
}

REFXMLNode* REFXMLElement::GetParentNode() const
{
	return m_pParentNode;
}

REFXMLDocument* REFXMLElement::GetOwnerDocument() const
{
	return m_pName->GetOwnerDocument();
}

REFXMLAttributeMap* REFXMLElement::GetAttributes() const
{
	if (m_pAttributes == NULL)
	{
		MutexHolder lock(&GetOwnerDocument()->m_docMutex);
		if (m_pAttributes == NULL)
		{
			REFXMLElement* pThis = const_cast<REFXMLElement*>(this);
			pThis->m_pAttributes = new REFXMLAttributeMap(pThis);
		}
	}
	return m_pAttributes;
}

// Gets or sets the concatenated values of the node and all its children.
UniString REFXMLElement::GetInnerText() const
{
	return REFXMLLinkedNode::GetInnerText();
}
void REFXMLElement::SetInnerText(const UniString& str)
{
	REFXMLLinkedNode* pLinkedNode = GetLastNode();
	if (pLinkedNode != NULL &&					//there is one child
		pLinkedNode->GetNodeType() == XMLNodeType_Text &&	//which is text node
		pLinkedNode->m_pNext == pLinkedNode)	// and it is the only child 
	{
		//this branch is for perf reason, event fired when TextNode.Value is changed.
		pLinkedNode->SetValue(str);
	}
	else
	{
		SetEmpty();
		AppendChild(GetOwnerDocument()->CreateTextNode(str));
	}
}

// Gets or sets the markup representing just the children of this node.
UniString REFXMLElement::GetInnerXml() const
{
	return REFXMLLinkedNode::GetInnerXml();
}

void REFXMLElement::SetInnerXml(const UniString& str)
{
	// Rmeove all children.
	SetEmpty();

	// TODO: LoadInnerXmlElement
	BFX_ASSERT(false);
}

REFXMLNode* REFXMLElement::GetNextSibling() const
{
	if (m_pParentNode != NULL && m_pParentNode->GetLastChild() != this)
		return m_pNext;
	return NULL;
}

// Saves the current node to the specified XmlWriter.
void REFXMLElement::WriteTo(REFXMLWriter* w) const
{
	w->WriteStartElement(GetPrefix(), GetLocalName(), GetNamespaceURI());
	REFXMLAttributeMap* pAttrs = GetAttributes();
	if (pAttrs->GetCount() > 0)
	{
		for (int i = 0; i < pAttrs->GetCount(); i ++)
		{
			REFXMLAttribute* pAttr = pAttrs->GetAttribute(i);
			pAttr->WriteTo(w);
		}
	}
	if (IsEmpty())
	{
		w->WriteEndElement();
	}
	else
	{
		WriteContentTo(w);
		w->WriteFullEndElement();
	}
}

// Saves all the children of the node to the specified XmlWriter.
void REFXMLElement::WriteContentTo(REFXMLWriter* w) const
{
	for (REFXMLNode* pNode = GetFirstChild(); pNode != NULL; pNode = pNode->GetNextSibling())
	{
		pNode->WriteTo(w);
	}
}

void REFXMLElement::RemoveAll()
{
	//remove all the children
	SetEmpty();
	//remove all the attributes
	RemoveAllAttributes();
}

// Removes all attributes from the element.
void REFXMLElement::RemoveAllAttributes()
{
	m_pAttributes->RemoveAll();
}

// Gets a value indicating whether the current node has any attributes.
bool REFXMLElement::HasAttributes() const
{
	if (m_pAttributes == NULL)
		return false;
	return (m_pAttributes->GetCount() > 0);
}

// Returns the value for the attribute with the specified name.
UniString REFXMLElement::GetAttribute(const UniString& strName) const
{
	REFXMLAttribute* pAttr = GetAttributeNode(strName);
	if (pAttr != NULL)
		return pAttr->GetValue();
	return UniString();
}

// Sets the value of the attribute with the specified name.
void REFXMLElement::SetAttribute(const UniString& strName, const UniString& strValue)
{
	REFXMLAttribute* pAttr = GetAttributeNode(strName);
	if (pAttr == NULL)
	{
		pAttr = GetOwnerDocument()->CreateAttribute(strName);
		pAttr->SetValue(strValue);
		GetAttributes()->AppendAttribute(pAttr);
	}
	else
	{
		pAttr->SetValue(strValue);
	}
}
// Removes an attribute by name.
void REFXMLElement::RemoveAttribute(const UniString& strName)
{
	if (HasAttributes())
		GetAttributes()->RemoveNamedItem(strName);
}

// Returns the XmlAttribute with the specified name.
REFXMLAttribute* REFXMLElement::GetAttributeNode(const UniString& strName) const
{
	REFXMLAttribute* pAttr = NULL;
	if (HasAttributes())
		pAttr = GetAttributes()->GetAttribute(strName);
	return pAttr;
}
// Adds the specified XmlAttribute.
REFXMLAttribute* REFXMLElement::SetAttributeNode(REFXMLAttribute* pNewAttr)
{
	BFX_REQUIRE (pNewAttr != NULL && pNewAttr->GetOwnerElement() == NULL && "The 'Attribute' node cannot be inserted because it is already an attribute of another element.");
	return (REFXMLAttribute*)GetAttributes()->SetNamedItem(pNewAttr);
}
// Removes the specified XmlAttribute.
REFXMLAttribute* REFXMLElement::RemoveAttributeNode(REFXMLAttribute* pOldAttr)
{
	REFXMLAttribute* pAttr = NULL;
	if (HasAttributes())
		pAttr = GetAttributes()->Remove(pOldAttr);
	return pAttr;
}

// Gets or sets whether the element does not have any children.
bool REFXMLElement::IsEmpty() const
{
	return (GetFirstChild() == NULL);
}

void REFXMLElement::SetEmpty()
{
	if (GetFirstChild() != NULL)
	{
		REFXMLLinkedNode::RemoveAll();
		m_pFirstChild = m_pLastChild = NULL;
	}
}

//
// DOM Level 2
//
// Returns the value for the attribute with the specified LocalName and NamespaceURI.
UniString REFXMLElement::GetAttribute(const UniString& strLocalName, const UniString& strNamespaceURI) const
{
	REFXMLAttribute* pAttr = GetAttributeNode(strLocalName, strNamespaceURI);
	if (pAttr != NULL)
		return pAttr->GetValue();
	return UniString();	// empty
}
// Sets the value of the attribute with the specified name and namespace.
UniString REFXMLElement::SetAttribute(const UniString& strLocalName, const UniString& strNamespaceURI, const UniString& strValue)
{
	REFXMLAttribute* pAttr = SetAttributeNode(strLocalName, strNamespaceURI);
	BFX_ASSERT(pAttr != NULL);
	pAttr->SetValue(strValue);
	return strValue;
}
// Removes an attribute specified by LocalName and NamespaceURI.
void REFXMLElement::RemoveAttribute(const UniString& strLocalName, const UniString& strNamespaceURI)
{
	RemoveAttributeNode(strLocalName, strNamespaceURI);
}

// Returns the XmlAttribute with the specified LocalName and NamespaceURI.
REFXMLAttribute* REFXMLElement::GetAttributeNode(const UniString& strLocalName, const UniString& strNamespaceURI) const
{
	if (HasAttributes())
		return GetAttributes()->GetAttribute(strLocalName, strNamespaceURI);
	return NULL;
}
// Adds the specified XmlAttribute.
REFXMLAttribute* REFXMLElement::SetAttributeNode(const UniString& strLocalName, const UniString& strNamespaceURI)
{
	REFXMLAttribute* pAttr = GetAttributeNode(strLocalName, strNamespaceURI);
	if (pAttr == NULL)
	{
		pAttr = GetOwnerDocument()->CreateAttribute(UniString(), strLocalName, strNamespaceURI);
		GetAttributes()->AppendAttribute(pAttr);
	}
	return pAttr;
}
// Removes the XmlAttribute specified by LocalName and NamespaceURI.
REFXMLAttribute* REFXMLElement::RemoveAttributeNode(const UniString& strLocalName, const UniString& strNamespaceURI)
{
	if (HasAttributes())
	{
		REFXMLAttribute* pAttr = GetAttributeNode(strLocalName, strNamespaceURI);
		GetAttributes()->Remove(pAttr);
		return pAttr;
	}
	return NULL;
}

// Returns a XmlNodeList containing
// a list of all descendant elements that match the specified name.
REFXMLNodeList* REFXMLElement::GetElementsByTagName(const UniString& strName) const
{
	REFXMLNodeList* pElemList = new REFXMLElementList(const_cast<REFXMLElement*>(this), strName);
	pElemList->Autorelease();
	return pElemList;
}

// Returns a XmlNodeList containing 
// a list of all descendant elements that match the specified name.
REFXMLNodeList* REFXMLElement::GetElementsByTagName(const UniString& strLocalName, const UniString& strNamespaceURI) const
{
	REFXMLNodeList* pElemList = new REFXMLElementList(const_cast<REFXMLElement*>(this), strLocalName, strNamespaceURI);
	pElemList->Autorelease();
	return pElemList;
}

bool REFXMLElement::IsContainer() const
{
	return true;
}

REFXMLLinkedNode* REFXMLElement::GetFirstNode() const
{
	return m_pFirstChild;
}

void REFXMLElement::SetFirstNode(REFXMLLinkedNode* pNode)
{
	m_pFirstChild = pNode;
}

REFXMLLinkedNode* REFXMLElement::GetLastNode() const
{
	return m_pLastChild;
}

void REFXMLElement::SetLastNode(REFXMLLinkedNode* pNode)
{
	m_pLastChild = pNode;
}

void REFXMLElement::SetParent(REFXMLNode* pNode)
{
	m_pParentNode = pNode;
}

bool REFXMLElement::IsValidChildType(XMLNodeType type) const
{
	switch (type)
	{
	case XMLNodeType_Element:
	case XMLNodeType_Text:
	case XMLNodeType_EntityReference:
	case XMLNodeType_Comment:
	case XMLNodeType_Whitespace:
	case XMLNodeType_SignificantWhitespace:
	case XMLNodeType_ProcessingInstruction:
	case XMLNodeType_CDATA:
		return true;

	default:
		return false;
	}
}

//the function is provided only at Load time to speed up Load process
REFXMLNode* REFXMLElement::AppendChildForLoad(REFXMLNode* pNewChild, REFXMLDocument* pDoc)
{
	REFXMLLinkedNode* pNewNode = (REFXMLLinkedNode*)pNewChild;
	if (m_pLastChild == NULL)
	{
		BFX_ASSERT(m_pFirstChild == NULL);
		m_pFirstChild = m_pLastChild = pNewNode;
	}
	else
	{
		REFXMLLinkedNode* pRefNode = m_pLastChild;
		pNewNode->m_pPrev = pRefNode;
		pRefNode->m_pNext = pNewNode;
		m_pLastChild = pNewNode;
		if (pRefNode->IsText() && pNewNode->IsText())
		{
			NestTextNodes(pRefNode, pNewNode);
			goto Return;
		}
	}
	pNewNode->SetParentForLoad(this);

Return:
	pDoc->NotifyChanged();
	return pNewNode;
}

REFXMLName* REFXMLElement::GetXmlName() const
{
	return m_pName;
}

void REFXMLElement::SetXmlName(REFXMLName* pName)
{
	m_pName = pName;
}

}	//	namespace BFX
